﻿import { ResourceConfigApi } from '@/api/infra/resource/config'
import type { MimetypeType, AllowMimetypeType } from '@/utils/file/type'
import { isString } from '@/utils/is'
export const fileExtList = ['7z', 'ai', 'cdr', 'class', 'css', 'csv', 'doc', 'dockerfile', 'docx', 'exe', 'gitignore', 'html', 'iso', 'jar', 'java', 'js', 'json', 'md', 'pdf', 'ppt', 'pptx', 'pr', 'psd', 'py', 'rar', 'sql', 'svg', 'txt', 'xls', 'xlsx', 'xml', 'yml', 'zip']
export const extTypeList = [
  { key: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'tiff', 'tif'], value: 'image' },
  { key: ['mp4', 'mov', 'avi', 'mkv', 'flv', '3gp', 'webm', 'm3u8'], value: 'video' },
  { key: ['mp3', 'wav', 'ogg', 'aac', 'flac', 'wma'], value: 'audio' },
  { key: ['txt', 'html', 'htm', 'css', 'js', 'csv', 'md', 'xml'], value: 'text' },
  { key: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'rar', 'exe', 'json', 'apk', '7z', 'ai', 'cdr', 'class', 'dockerfile', 'gitignore', 'iso', 'jar', 'java', 'pr', 'psd', 'py', 'sql', 'yml'], value: 'application' }
]

export const mimetypeList = ['image', 'video', 'audio', 'text', 'application']

export type ConfigMimetypeType = {
  image?: AcceptSizeType
  video?: AcceptSizeType
  audio?: AcceptSizeType
  text?: AcceptSizeType
  application?: AcceptSizeType
}

export type AcceptSizeType = {
  accept: string
  size: number
  sizeText: string
  helpText: string
  helpTextLine: string
  config?: ConfigMimetypeType
}

export type ConfigType = {
  imageExts: string
  imageSize: number
  videoExts: string
  videoSize: number
  audioExts: string
  audioSize: number
  textExts: string
  textSize: number
  applicationExts: string
  applicationSize: number
}

export const openWindow = (
  url: string,
  opt?: {
    target?: '_self' | '_blank' | string
    noopener?: boolean
    noreferrer?: boolean
  }
) => {
  const { target = '__blank', noopener = true, noreferrer = true } = opt || {}
  const feature: string[] = []

  noopener && feature.push('noopener=yes')
  noreferrer && feature.push('noreferrer=yes')

  window.open(url, target, feature.join(','))
}

/**
 * @description: base64 to blob
 */
export const dataURLtoBlob = (base64Buf: string): Blob => {
  const arr = base64Buf.split(',')
  const typeItem = arr[0]
  const mime = typeItem.match(/:(.*?);/)![1]
  const bstr = window.atob(arr[1])
  let n = bstr.length
  const u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new Blob([u8arr], { type: mime })
}

/**
 * img url to base64
 * @param url
 */
export const urlToBase64 = (url: string, mineType?: string): Promise<string> => {
  return new Promise((resolve, reject) => {
    let canvas = document.createElement('CANVAS') as Nullable<HTMLCanvasElement>
    const ctx = canvas!.getContext('2d')

    const img = new Image()
    img.crossOrigin = 'anonymous'
    img.src = url + '?timestamp=' + Date.now()
    img.onload = function () {
      try {
        if (!canvas || !ctx) {
          return reject()
        }
        canvas.height = img.naturalHeight
        canvas.width = img.naturalWidth
        ctx.drawImage(img, 0, 0)
        const dataURL = canvas.toDataURL(mineType || 'image/png')
        canvas = null
        resolve(dataURL)
      } catch (error) {
        reject(error)
      }
    }
    img.onerror = err => {
      canvas = null
      reject(new Error('image Error:' + err))
    }
  })
}

// 文件转base64
export function getBase64ByFile(file: File) {
  return new Promise<{
    result: string
    file: File
  }>((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = () => resolve({ result: reader.result as string, file })
    reader.onerror = error => reject(error)
  })
}

// 根据上传视频或音频文件，获取额外参数（宽、高、时长）
export const getDurationByMedia = async file => {
  if (!file) return false
  let fileMimetype = getMimetype(file.name)
  if (fileMimetype !== 'video' && fileMimetype !== 'audio') return false
  const checkMedia = document.getElementById('check-media')
  if (checkMedia) {
    document.body.removeChild(checkMedia)
  }
  let doms
  if (fileMimetype === 'video') {
    doms = document.createElement('video')
  } else {
    doms = document.createElement('audio')
  }
  const url = URL.createObjectURL(file)
  doms.src = url
  doms.id = 'check-media'
  doms.style.display = 'none'
  document.body.appendChild(doms)
  return await getDuration(doms, fileMimetype)
}

const getDuration = (doms, mimetype) => {
  // 由于loadedmetadata 是异步代码所以需要promise进行封装转换为同步代码执行
  const promise = new Promise(resolve => {
    doms.addEventListener('loadedmetadata', e => {
      console.log(doms, 'dom对象')
      console.log(e, '对象')
      const obj = {
        width: 0,
        height: 0,
        duration: 0 // 时长 1表示一秒
      }
      if (mimetype === 'video') {
        obj.width = doms.videoWidth
        obj.height = doms.videoHeight
        obj.duration = e.target.duration
      } else {
        obj.duration = e.target.duration
      }
      resolve(obj)
    })
  })
  return promise
}

// 根据上传图片文件获取图片的实际宽高
export const getImageSizeByFile = file => {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.onload = () => {
      resolve({
        width: img.naturalWidth,
        height: img.naturalHeight
      })
      URL.revokeObjectURL(img.src)
    }
    img.onerror = reject
    img.src = URL.createObjectURL(file)
  })
}

// 根据上传图片文件获取图片数据
export const getImageDataByFile = file => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = function (e) {
      resolve((e.target?.result as string) ?? '')
    }
    reader.onerror = reject
  })
}

/**
 * Download online pictures
 * @param url
 * @param filename
 * @param mime
 * @param bom
 */
export const downloadByOnlineUrl = (url: string, filename: string, mime?: string, bom?: BlobPart) => {
  urlToBase64(url).then(base64 => {
    downloadByBase64(base64, filename, mime, bom)
  })
}

/**
 * Download pictures based on base64
 * @param buf
 * @param filename
 * @param mime
 * @param bom
 */
export const downloadByBase64 = (buf: string, filename: string, mime?: string, bom?: BlobPart) => {
  const base64Buf = dataURLtoBlob(buf)
  downloadByData(base64Buf, filename, mime, bom)
}

/**
 * Download according to the background interface file stream
 * @param {*} data
 * @param {*} filename
 * @param {*} mime
 * @param {*} bom
 */
export const downloadByData = (data: BlobPart, filename: string, mime?: string, bom?: BlobPart) => {
  const blobData = typeof bom !== 'undefined' ? [bom, data] : [data]
  const blob = new Blob(blobData, { type: mime || 'application/octet-stream' })

  const blobURL = window.URL.createObjectURL(blob)
  const tempLink = document.createElement('a')
  tempLink.style.display = 'none'
  tempLink.href = blobURL
  tempLink.setAttribute('download', filename)
  if (typeof tempLink.download === 'undefined') {
    tempLink.setAttribute('target', '_blank')
  }
  document.body.appendChild(tempLink)
  tempLink.click()
  document.body.removeChild(tempLink)
  window.URL.revokeObjectURL(blobURL)
}

/**
 * Download file according to file address
 * @param {*} sUrl
 */
export const downloadByUrl = ({ url, target = '_blank', fileName }: { url: string; target?: '_self' | '_blank'; fileName?: string }): boolean => {
  const isChrome = window.navigator.userAgent.toLowerCase().indexOf('chrome') > -1
  const isSafari = window.navigator.userAgent.toLowerCase().indexOf('safari') > -1

  if (/(iP)/g.test(window.navigator.userAgent)) {
    console.error('Your browser does not support download!')
    return false
  }
  if (isChrome || isSafari) {
    const link = document.createElement('a')
    link.href = url
    link.target = target

    if (link.download !== undefined) {
      link.download = fileName || url.substring(url.lastIndexOf('/') + 1, url.length)
    }

    if (document.createEvent) {
      const e = document.createEvent('MouseEvents')
      e.initEvent('click', true, true)
      link.dispatchEvent(e)
      return true
    }
  }
  if (url.indexOf('?') === -1) {
    url += '?download'
  }

  openWindow(url, { target })
  return true
}

/**
 * element plus 的文件大小 Formatter 实现
 *
 * @param row 行数据
 * @param column 字段
 * @param cellValue 字段值
 */
// @ts-ignore
export const fileSizeFormatter = (row, column, cellValue) => {
  const { t } = useI18n() // 国际化
  const fileSize = cellValue
  if (fileSize === 0) return t('infra.resource.config', 'size')
  const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
  const srcSize = parseFloat(fileSize)
  const index = Math.floor(Math.log(srcSize) / Math.log(1024))
  const size = srcSize / Math.pow(1024, index)
  const sizeStr = size.toFixed(2) //保留的小数位数
  return sizeStr + ' ' + unitArr[index]
}

/**
 * 文件大小 Formatter 实现
 * @param value 字段值
 */
// @ts-ignore
export const filesizeFormatter = value => {
  const fileSize = value
  if (fileSize === 0) return '0'
  const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
  const srcSize = parseFloat(fileSize)
  const index = Math.floor(Math.log(srcSize) / Math.log(1024))
  const size = srcSize / Math.pow(1024, index)
  const sizeStr = size.toFixed(2) //保留的小数位数
  return sizeStr + ' ' + unitArr[index]
}

/**
 * 文件大小 Formatter 实现，转化为指定目标单位大小
 * @param value 字段值
 * @param unit 目标单位
 */
export const filesizeConvertToTargetUnit = (value, targetUnit) => {
  const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
  const srcSize = parseFloat(value)

  if (srcSize === 0) {
    return 0
  }

  let index

  if (targetUnit !== undefined) {
    index = unitArr.indexOf(targetUnit)
    if (index === -1) {
      throw new Error(`Invalid unit: ${targetUnit}`)
    }
  } else {
    index = Math.floor(Math.log(srcSize) / Math.log(1024))
    index = Math.max(0, Math.min(index, unitArr.length - 1))
  }
  const size = srcSize / Math.pow(1024, index)
  const sizeStr = size.toFixed(2)
  return parseFloat(sizeStr)
}

/**
 * 文件大小 Formatter 实现， 将指定单位大小转化为字节大小
 * @param value 字段值
 * @param unit 值单位
 */
export const filesizeConvertToBytes = (value, unit) => {
  const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']

  // 解析输入值并验证有效性
  const numericValue = parseFloat(value)
  if (isNaN(numericValue)) {
    throw new Error('Invalid value: must be a number')
  }

  // 查找单位索引并验证有效性
  const unitIndex = unitArr.indexOf(unit)
  if (unitIndex === -1) {
    throw new Error(`Invalid unit: ${unit}`)
  }

  // 计算字节大小
  return numericValue * Math.pow(1024, unitIndex)
}

// 获取主资源配置
export const getFileConfig = async () => {
  return await ResourceConfigApi.master()
}

// 获取配置相关参数
export const getAcceptSizeByAccept = async (accept: string) => {
  const config = await getFileConfig()
  // 文件类型配置
  // let config: ConfigType = {
  //   imageExts: '.jpeg,.jpg,.png,.gif',
  //   imageSize: 5 * 1024 * 1024, // 5 * 1024 * 1024
  //   videoExts: '.mp4,.avi',
  //   videoSize: 10 * 1024 * 1024 * 1024, // 10 * 1024 * 1024 * 1024
  //   audioExts: '.mp3',
  //   audioSize: 30 * 1024 * 1024, // 30 * 1024 * 1024
  //   textExts: '.txt,.html,.htm,.css,.js,.csv,.md,.xml',
  //   textSize: 10 * 1024 * 1024, // 10 * 1024 * 1024
  //   applicationExts: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.zip,.rar,.7z',
  //   applicationSize: 200 * 1024 * 1024 // 200 * 1024 * 1024
  // }
  const realConfig = getExtMimetypeConfig(accept)
  if (isString(realConfig.mimetype)) {
    config[`${realConfig.mimetype}Exts`] = realConfig.config[realConfig.mimetype]
  } else {
    realConfig.mimetype.forEach(item => {
      config[`${item}Exts`] = realConfig.config[item]
    })
  }
  return getAcceptSizeByType(config, realConfig.mimetype)
}

// 获取配置相关参数
export const getAcceptSize = async (type?: MimetypeType) => {
  const config = await getFileConfig()
  // 文件类型配置
  // let config: ConfigType = {
  //   imageExts: '.jpeg,.jpg,.png,.gif',
  //   imageSize: 5 * 1024 * 1024, // 5 * 1024 * 1024
  //   videoExts: '.mp4,.avi',
  //   videoSize: 10 * 1024 * 1024 * 1024, // 10 * 1024 * 1024 * 1024
  //   audioExts: '.mp3',
  //   audioSize: 30 * 1024 * 1024, // 30 * 1024 * 1024
  //   textExts: '.txt,.html,.htm,.css,.js,.csv,.md,.xml',
  //   textSize: 10 * 1024 * 1024, // 10 * 1024 * 1024
  //   applicationExts: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.zip,.rar,.7z',
  //   applicationSize: 200 * 1024 * 1024 // 200 * 1024 * 1024
  // }
  return getAcceptSizeByType(config, type)
}

const getAcceptSizeByType = (config, type) => {
  const { t } = useI18n() // 国际化
  if (type) {
    if (isString(type)) {
      return {
        accept: config[`${type}Exts`],
        size: config[`${type}Size`],
        sizeText: filesizeFormatter(config[`${type}Size`]),
        helpText: helpText(config[`${type}Exts`], filesizeFormatter(config[`${type}Size`])),
        helpTextLine: config[`${type}Size`] === 0 ? t('upload', 'acceptHtml', [config[`${type}Exts`]]) : t('upload', 'tips', [config[`${type}Exts`], filesizeFormatter(config[`${type}Size`])])
      }
    } else {
      const accept: string[] = []
      const size: number[] = []
      type.forEach(item => {
        accept.push(config[`${item}Exts`])
        size.push(config[`${item}Size`])
      })
      const zero = size.some(item => item === 0)
      return {
        accept: accept.join(','),
        size: zero ? 0 : Math.max(...size),
        sizeText: filesizeFormatter(Math.max(...size)),
        helpText: helpText(accept.join(','), filesizeFormatter(Math.max(...size))),
        helpTextLine: zero ? t('upload', 'acceptHtml', [accept.join(',')]) : t('upload', 'tips', [accept.join(','), filesizeFormatter(Math.max(...size))]),
        config: getAcceptSizeMimetypeConfig(config, type)
      }
    }
  } else {
    const accept = [config.imageExts, config.videoExts, config.audioExts, config.textExts, config.applicationExts]
    const size = [config.imageSize, config.videoSize, config.audioSize, config.textSize, config.applicationSize]
    const zero = size.some(item => item === 0)
    return {
      accept: accept.join(','),
      size: zero ? 0 : Math.max(...size),
      sizeText: filesizeFormatter(Math.max(...size)),
      helpText: helpText(accept.join(','), filesizeFormatter(Math.max(...size))),
      helpTextLine: zero ? t('upload', 'acceptHtml', [accept.join(',')]) : t('upload', 'tips', [accept.join(','), filesizeFormatter(Math.max(...size))]),
      config: getAcceptSizeMimetypeConfig(config, type)
    }
  }
}

export const getAcceptSizeMimetypeConfig = (config: ConfigType, type: AllowMimetypeType[] | '' | undefined) => {
  const { t } = useI18n() // 国际化
  let res: ConfigMimetypeType = {}
  if (type) {
    type.forEach(item => {
      res[item] = {
        accept: config[`${item}Exts`],
        size: config[`${item}Size`],
        sizeText: filesizeFormatter(config[`${item}Size`]),
        helpText: helpText(config[`${item}Exts`], filesizeFormatter(config[`${item}Size`])),
        helpTextLine: config[`${item}Size`] === 0 ? t('upload', 'acceptHtml', [config[`${item}Exts`]]) : t('upload', 'tips', [config[`${item}Exts`], filesizeFormatter(config[`${item}Size`])])
      }
    })
  } else {
    mimetypeList.forEach(item => {
      res[item] = {
        accept: config[`${item}Exts`],
        size: config[`${item}Size`],
        sizeText: filesizeFormatter(config[`${item}Size`]),
        helpText: helpText(config[`${item}Exts`], filesizeFormatter(config[`${item}Size`])),
        helpTextLine: config[`${item}Size`] === 0 ? t('upload', 'acceptHtml', [config[`${item}Exts`]]) : t('upload', 'tips', [config[`${item}Exts`], filesizeFormatter(config[`${item}Size`])])
      }
    })
  }
  return res
}

// 提示文本组装
const helpText = (accept: string, sizeText: string) => {
  const { t } = useI18n() // 国际化
  const helpTexts: string[] = []
  helpTexts.push(t('upload', 'acceptHtml', [accept]))
  if (sizeText) {
    helpTexts.push(t('upload', 'maxSizeHtml', [sizeText]))
  }
  return helpTexts.join('')
}

// 判断资源的 mimetype 类型
export const getMimetype = (url: string): AllowMimetypeType => {
  if (url.lastIndexOf('.') === -1) return 'application'
  let ext = getFileExt(url)
  if (!ext) {
    ext = 'application'
  }
  let mimetype: AllowMimetypeType = 'application'
  extTypeList.forEach(item => {
    const hasExt = item.key.some(v => {
      return v === ext
    })
    if (hasExt) {
      mimetype = item.value as AllowMimetypeType
    }
  })
  return mimetype
}

// 获取资源拓展名
export const getFileExt = (url: string) => {
  if (url.lastIndexOf('.') === -1) return undefined
  if (url.substring(url.lastIndexOf('.') + 1).indexOf('?') > 0) {
    return url.substring(url.lastIndexOf('.') + 1).split('?')[0]
  } else {
    if (url.substring(url.lastIndexOf('.') + 1).indexOf('&') > 0) {
      return url.substring(url.lastIndexOf('.') + 1).split('&')[0]
    } else {
      return url.substring(url.lastIndexOf('.') + 1)
    }
  }
}

// 判断扩展名的 mimetype 类型
export const getExtMimetype = (ext: string): AllowMimetypeType => {
  if (ext.indexOf('/') !== -1) return ext.split('/')[0] as AllowMimetypeType
  let extNew = ext
  if (ext.indexOf('.') !== -1) {
    extNew = ext.substring(ext.lastIndexOf('.') + 1)
  }
  let mimetype: AllowMimetypeType = 'application'
  extTypeList.forEach(item => {
    const hasExt = item.key.some(v => {
      return v === extNew
    })
    if (hasExt) {
      mimetype = item.value as AllowMimetypeType
    }
  })
  return mimetype
}

// 根据给定的接受类型，推断出其所属 mimetype
export const getExtMimetypeConfig = (accept: string) => {
  let extList: string[] = []
  if (accept.includes(',')) {
    extList = accept.split(',')
  } else {
    extList = [accept]
  }
  let config: any = {
    image: [],
    video: [],
    audio: [],
    text: [],
    application: []
  }
  const mimetypeList: AllowMimetypeType[] = []
  extList.forEach(item => {
    const mimetype = getExtMimetype(item)
    if (!mimetypeList.includes(mimetype)) {
      mimetypeList.push(mimetype)
    }
    config[`${mimetype}`].push(item)
  })
  return {
    mimetype: mimetypeList.length === 1 ? mimetypeList[0] : mimetypeList,
    config: {
      image: config.image.join(','),
      video: config.video.join(','),
      audio: config.audio.join(','),
      text: config.text.join(','),
      application: config.application.join(',')
    }
  }
}

// 获取资源名称
export const getFileName = (url: string) => {
  if (url.lastIndexOf('/') === -1) return ''
  if (url.substring(url.lastIndexOf('/') + 1).indexOf('?') > 0) {
    return url.substring(url.lastIndexOf('/') + 1).split('?')[0]
  } else {
    if (url.substring(url.lastIndexOf('/') + 1).indexOf('&') > 0) {
      return url.substring(url.lastIndexOf('/') + 1).split('&')[0]
    } else {
      return url.substring(url.lastIndexOf('/') + 1)
    }
  }
}
